<?php

namespace app\common\helpers;

define('X_PI', 3.14159265358979324 * 3000.0 / 180.0);

class CoordinateTool
{
    private static $pi = 3.1415926535897932384626;  // 圆周率
    private static $a = 6378245.0; // WGS 长轴半径
    private static $ee = 0.00669342162296594323; // WGS 偏心率的平方

    /**
     * 将火星坐标系GCJ-02 坐标 转换成百度坐标系 BD-09 坐标
     * @param $gc_loc 火星坐标点(Class Coordinate)
     * @return $bg_loc Coordinate对象，百度地图经纬度坐标
     */
    public static function gcj_bd($gc_loc)
    {
        $x_pi = X_PI;
        $x = $gc_loc->x;
        $y = $gc_loc->y;
        $z = sqrt($x * $x + $y * $y) + 0.00002 * sin($y * $x_pi);
        $theta = atan2($y, $x) + 0.000003 * cos($x * $x_pi);
        $bd_x = $z * cos($theta) + 0.0065;
        $bd_y = $z * sin($theta) + 0.006;
        $bg_loc = new Coordinate($bd_x, $bd_y);
        return $bg_loc;
    }

    /**
     * 将百度坐标系 BD-09 坐标 转换成 火星坐标系GCJ-02 坐标
     * @param $bd_loc 火星坐标点(Class Coordinate)
     * @return $bg_loc Coordinate对象，火星坐标系经纬度坐标
     */
    public static function bd_gcj($bd_loc)
    {
        $x_pi = X_PI;
        $x = $bd_loc->x - 0.0065;
        $y = $bd_loc->y - 0.006;
        $z = sqrt($x * $x + $y * $y) - 0.00002 * sin($y * $x_pi);
        $theta = atan2($y, $x) - 0.000003 * cos($x * $x_pi);
        $gc_x = $z * cos($theta);
        $gc_y = $z * sin($theta);
        $gc_loc = new Coordinate($gc_x, $gc_y);
        return $gc_loc;
    }

    /**
     * 将国际通用坐标系WGS84坐标 转换成 火星坐标系GCJ-02 坐标
     * @param $wgs_loc WGS84坐标点(Class Coordinate)
     * @return $bg_loc Coordinate对象，火星坐标系经纬度坐标
     */
    public static function wgs_gcj($wgs_loc)
    {
        $wgs_lon = $wgs_loc->x;
        $wgs_lat = $wgs_loc->y;

        if (self::outOfChina($wgs_lon, $wgs_lat)) {
            return (new Coordinate($wgs_lon, $wgs_lat));
        }
        $x_pi = X_PI;
        $dLat = self::transformLat($wgs_lon - 105.0, $wgs_lat - 35.0);
        $dLon = self::transformLon($wgs_lon - 105.0, $wgs_lat - 35.0);
        $radLat = $wgs_lat / 180.0 * self::$pi;
        $magic = sin($radLat);
        $magic = 1 - self::$ee * $magic * $magic;
        $sqrtMagic = sqrt($magic);
        $dLat = ($dLat * 180.0) / ((self::$a * (1 - self::$ee)) / ($magic * $sqrtMagic) * self::$pi);
        $dLon = ($dLon * 180.0) / (self::$a / $sqrtMagic * cos($radLat) * self::$pi);

        $mgLat = $wgs_lat + $dLat;
        $mgLon = $wgs_lon + $dLon;

        $gcj_loc = new Coordinate($mgLon, $mgLat);
        return $gcj_loc;
    }

    /**
     * 将火星坐标系GCJ-02坐标 转换成 国际通用坐标系WGS84坐标
     * @param $gcj_loc GCJ-02坐标点(Class Coordinate)
     * @return $wgs_loc Coordinate对象，国际通用坐标系WGS84坐标
     */
    public static function gcj_wgs($gcj_loc)
    {
        $to = self::wgs_gcj($gcj_loc);
        $lon = $gcj_loc->x;
        $lat = $gcj_loc->y;
        $gcl_lon = $to->x;
        $gcl_lat = $to->y;
        $d_lat = $gcl_lat - $lat;
        $d_lon = $gcl_lon - $lon;
        return new Coordinate($lon - $d_lon, $lat - $d_lat);
    }


    private static function outOfChina($lon, $lat)
    {
        if ($lon < 72.004 || $lon > 137.8347)
            return true;
        if ($lat < 0.8293 || $lat > 55.8271)
            return true;

        return false;
    }

    private static function transformLat($x, $y)
    {
        $ret = -100.0 + 2.0 * $x + 3.0 * $y + 0.2 * $y * $y + 0.1 * $x * $y + 0.2 * sqrt(abs($x));
        $ret += (20.0 * sin(6.0 * $x * self::$pi) + 20.0 * sin(2.0 * $x * self::$pi)) * 2.0 / 3.0;
        $ret += (20.0 * sin($y * self::$pi) + 40.0 * sin($y / 3.0 * self::$pi)) * 2.0 / 3.0;
        $ret += (160.0 * sin($y / 12.0 * self::$pi) + 320 * sin($y * self::$pi / 30.0)) * 2.0 / 3.0;
        return $ret;
    }

    private static function transformLon($x, $y)
    {
        $ret = 300.0 + $x + 2.0 * $y + 0.1 * $x * $x + 0.1 * $x * $y + 0.1 * sqrt(abs($x));
        $ret += (20.0 * sin(6.0 * $x * self::$pi) + 20.0 * sin(2.0 * $x * self::$pi)) * 2.0 / 3.0;
        $ret += (20.0 * sin($x * self::$pi) + 40.0 * sin($x / 3.0 * self::$pi)) * 2.0 / 3.0;
        $ret += (150.0 * sin($x / 12.0 * self::$pi) + 300.0 * sin($x / 30.0 * self::$pi)) * 2.0 / 3.0;

        return $ret;
    }


    public static function gcj02towgs84($lng, $lat)
    {

        $lng = +$lng;
        $lat = +$lat;
        if (self::outOfChina($lng, $lat)) {
            return [$lng, $lat];
        }

        $dLat = self::transformLat2($lng - 105.0, $lat - 35.0);
        $dLon = self::transformlng2($lng - 105.0, $lat - 35.0);
        $radLat = $lat / 180.0 * self::$pi;
        $magic = sin($radLat);
        $magic = 1 - self::$ee * $magic * $magic;
        $sqrtMagic = sqrt($magic);
        $dLat = ($dLat * 180.0) / ((self::$a * (1 - self::$ee)) / ($magic * $sqrtMagic) * self::$pi);
        $dLon = ($dLon * 180.0) / (self::$a / $sqrtMagic * cos($radLat) * self::$pi);

        $mglat = $lat + $dLat;
        $mglng = $lng + $dLon;

        return [$lng * 2 - $mglng, $lat * 2 - $mglat];
    }

    private static function transformLat2($lng, $lat)
    {
        $lng = +$lng;
        $lat = +$lat;
        $ret = -100.0 + 2.0 * $lng + 3.0 * $lat + 0.2 * $lat * $lat + 0.1 * $lng * $lat + 0.2 * sqrt(abs($lng));

        $ret += (20.0 * sin(6.0 * $lng * self::$pi) + 20.0 * sin(2.0 * $lng * self::$pi)) * 2.0 / 3.0;
        $ret += (20.0 * sin($lat * self::$pi) + 40.0 * sin($lat / 3.0 * self::$pi)) * 2.0 / 3.0;
        $ret += (160.0 * sin($lat / 12.0 * self::$pi) + 320 * sin($lat * self::$pi / 30.0)) * 2.0 / 3.0;

        return $ret;
    }

    private static function transformlng2($lng, $lat)
    {
        $lng = +$lng;
        $lat = +$lat;
        $ret = 300.0 + $lng + 2.0 * $lat + 0.1 * $lng * $lng + 0.1 * $lng * $lat + 0.1 * sqrt(abs($lng));
        $ret += (20.0 * sin(6.0 * $lng * self::$pi) + 20.0 * sin(2.0 * $lng * self::$pi)) * 2.0 / 3.0;
        $ret += (20.0 * sin($lng * self::$pi) + 40.0 * sin($lng / 3.0 * self::$pi)) * 2.0 / 3.0;
        $ret += (150.0 * sin($lng / 12.0 * self::$pi) + 300.0 * sin($lng / 30.0 * self::$pi)) * 2.0 / 3.0;
        return $ret;
    }

    /**
     * 根据经纬度获取一个范围
     * @param $lng
     * @param $lat
     * @param float $distance
     * @param int $eatch_radius
     * @return array[]
     */
    public static function returnSquarePoint($lng, $lat, $distance = 0.5, $eatch_radius = 6371)
    {
        $dlng = 2 * asin(sin($distance / (2 * $eatch_radius)) / cos(deg2rad($lat)));
        $dlng = rad2deg($dlng);
        $dlat = $distance / $eatch_radius;
        $dlat = rad2deg($dlat);
        return [
            'left-top' => ['lat' => $lat + $dlat, 'lng' => $lng - $dlng],
            'right-top' => ['lat' => $lat + $dlat, 'lng' => $lng + $dlng],
            'left-bottom' => ['lat' => $lat - $dlat, 'lng' => $lng - $dlng],
            'right-bottom' => ['lat' => $lat - $dlat, 'lng' => $lng + $dlng]
        ];
    }

    /**
     * 根据经纬度计算距离 单位为公里
     * @param $lng1
     * @param $lat1
     * @param $lng2
     * @param $lat2
     * @return float
     */
    public static function getDistance($lng1, $lat1, $lng2, $lat2)
    {
        $radLat1 = deg2rad($lat1);
        $radLat2 = deg2rad($lat2);
        $radLng1 = deg2rad($lng1);
        $radLng2 = deg2rad($lng2);
        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;
        return 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2))) * 6378.137;
    }

    /**
     * 输入多个经纬度坐标 找出中心点
     * @param $data
     * @return array|false
     */
    public static function getCenterFromDegrees($data)
    {
        if (!is_array($data)) return [];
        $num_coords = count($data);
        $X = 0.0;
        $Y = 0.0;
        $Z = 0.0;
        foreach ($data as $coord) {
            $lon = deg2rad((float)$coord[0]);
            $lat = deg2rad((float)$coord[1]);

            $a = cos($lat) * cos($lon);
            $b = cos($lat) * sin($lon);
            $c = sin($lat);

            $X += $a;
            $Y += $b;
            $Z += $c;
        }

        $X /= $num_coords;
        $Y /= $num_coords;
        $Z /= $num_coords;

        $lon = atan2($Y, $X);
        $hyp = sqrt($X * $X + $Y * $Y);
        $lat = atan2($Z, $hyp);
        return [rad2deg($lon), rad2deg($lat)];
    }
}